package org.zxp.doc4j;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.docx4j.Docx4J;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.finders.RangeFinder;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.jaxb.Context;
import org.docx4j.model.fields.merge.DataFieldName;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;

import java.io.*;
import java.net.URL;
import java.util.*;

/**
 * @program: effectiveJava
 * @description:
 * @author: X-Pacific zhang
 * @create: 2020-09-01 15:10
 **/
@Slf4j
public class Test {

    private final static String CONTRACTKEY_DISPLAY = "CFCA_";
    private final static Boolean DELETE_BOOKMARK = true;
    private static org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory();
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            long begin = System.currentTimeMillis();
            c();
            long end = System.currentTimeMillis();
            System.out.println(end-begin);
        }
    }

    private static void c()throws Exception {
        WordReplaceBookmarkQo bookmarkQo = new WordReplaceBookmarkQo();
        List<BookMarkReplacement> listbookmark = new ArrayList<>();
        Map<DataFieldName, String> replaceBookmarkMap = new HashMap<DataFieldName, String>();
        // ============ 替换固定的标签 接口 开始 ============
        Calendar cal = Calendar.getInstance();
        String contractNo = "CFCA_key202001070001";
        listbookmark.add(new BookMarkReplacement("No", contractNo));
        //  固定替换 年月日
        listbookmark.add(new BookMarkReplacement("year", "" + cal.get(Calendar.YEAR)));
        listbookmark.add(new BookMarkReplacement("month", "" + (cal.get(Calendar.MONTH) + 1)));
        listbookmark.add(new BookMarkReplacement("day", "" + cal.get(Calendar.DAY_OF_MONTH)));
        listbookmark.add(new BookMarkReplacement("reg_date", "2020-09-01"));

        if (bookmarkQo.getBookmarkReplacement() != null) {
            listbookmark.addAll(bookmarkQo.getBookmarkReplacement());
        }
        // ============ 替换固定的标签 接口 结束 ============
        // 利用链表的顺序 和 DataFieldName name取hash 来实现自定义书签覆盖
        for(BookMarkReplacement bookmark : listbookmark){
            replaceBookmarkMap.put(new DataFieldName(bookmark.getBookmarkName()), bookmark.getReplacement());
        }
        File f = new File("D://d7547c00-ed08-40ed-abd3-57a623ded0e0.docx");
        InputStream targetStream = new FileInputStream(f);
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(targetStream);
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
        // ========= replace bookmark =============
        org.docx4j.wml.Document jaxbElement = (org.docx4j.wml.Document) documentPart.getJaxbElement();
        Body body = jaxbElement.getBody();
        replaceBookmarkContents(body.getContent(), replaceBookmarkMap);

        ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
        Docx4J.toPDF(wordMLPackage, pdfOutputStream);
        // ========= 本地缓存字体 =========
        FOSettings foSettings = setFonts(wordMLPackage);
        Docx4J.toFO(foSettings, pdfOutputStream, Docx4J.FLAG_EXPORT_PREFER_XSL);
    }

    private static void replaceBookmarkContents(List<Object> docxBody, Map<DataFieldName, String> replaceBookmarkMap)
            throws Exception {
        // doc 元素查找器
        RangeFinder rangeFinder = new RangeFinder("CTBookmark", "CTMarkupRange");
        new TraversalUtil(docxBody, rangeFinder);
        List<CTBookmark> bookmarkInDocList = rangeFinder.getStarts();

        for (CTBookmark bookmarkInDoc : bookmarkInDocList) {
            log.info("bookmarkInDoc.getName == {}", bookmarkInDoc.getName());
        }

        for (CTBookmark bookmarkInDoc : bookmarkInDocList) {
            log.info("bookmarkInDoc.name :{}", bookmarkInDoc.getName());
            // do we have data for this one?
            if (bookmarkInDoc.getName() == null)
                continue;
            String bookMarkReplaceStr = replaceBookmarkMap.get(new DataFieldName(bookmarkInDoc.getName()));
            // null 表示标签没有匹配中
            if (bookMarkReplaceStr == null)
                continue;

            log.info("bmInfo:vaild [匹配中标签:{}，准备替换]", bookmarkInDoc.getName());
            log.info("bmInfo:name:{},id:{}", bookmarkInDoc.getName(), bookmarkInDoc.getId());

            List<Object> bookmarkBrotherList = null;
            if (bookmarkInDoc.getParent() instanceof P) {
                bookmarkBrotherList = ((ContentAccessor) (bookmarkInDoc.getParent())).getContent();
            } else {
                continue;
            }

            int rangeStart = -1;
            int rangeEnd = -1;
            int i = 0;
            for (Object ox : bookmarkBrotherList) {
                Object listEntry = XmlUtils.unwrap(ox);
                if (listEntry.equals(bookmarkInDoc)) {
                    if (DELETE_BOOKMARK) {
                        rangeStart = i;
                    } else {
                        rangeStart = i + 1;
                    }
                } else if (listEntry instanceof CTMarkupRange) {
                    if (((CTMarkupRange) listEntry).getId().equals(bookmarkInDoc.getId())) {
                        if (DELETE_BOOKMARK) {
                            rangeEnd = i;
                        } else {
                            rangeEnd = i > rangeStart ? i - 1 : i; // handle empty bookmark case
                        }
                        break;
                    }
                }
                i++;
            }

            if (rangeStart > 0 && rangeEnd >= rangeStart) {
                // Delete the bookmark range
                if (rangeEnd > rangeStart) {
                    for (int j = rangeEnd; j >= rangeStart; j--) {
                        bookmarkBrotherList.remove(j);
                    }
                }
                // now add a run
                org.docx4j.wml.R r = factory.createR();
                org.docx4j.wml.Text t = factory.createText();
                t.setValue(bookMarkReplaceStr);
                r.getContent().add(t);

                RPr rpr = factory.createRPr();
                U u = factory.createU();
                u.setVal(UnderlineEnumeration.SINGLE);
                rpr.setU(u);
                r.setRPr(rpr);

                bookmarkBrotherList.add(rangeStart, r);
            }

        }
    }

    private static FOSettings setFonts(WordprocessingMLPackage mlPackage) throws Exception {
        String simsunFontFilePath = "file:///D:/simsun.ttc";
        Mapper fontMapper = new IdentityPlusMapper();
        PhysicalFonts.addPhysicalFonts("SimSun", new URL(simsunFontFilePath));
        PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
        PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
        PhysicalFonts.put("Malgun Gothic", PhysicalFonts.get("SimSun"));
        PhysicalFonts.put("仿宋", PhysicalFonts.get("SimSun"));
        PhysicalFonts.put("Calibri", PhysicalFonts.get("SimSun"));
        mlPackage.setFontMapper(fontMapper);
        FOSettings foSettings = Docx4J.createFOSettings();
        foSettings.setWmlPackage(mlPackage);
        return foSettings;
    }
}
